package cn.com.dbappsecurity.frame.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;

import cn.com.dbappsecurity.frame.annotations.Transactional;
import cn.com.dbappsecurity.utils.MybatisUtil;

/**
 * 动态代理类
 * @author dhz
 */
public class FrameProxy {

	public static final ThreadLocal<SqlSession> THREAD_LOCAL = new ThreadLocal<SqlSession>();
	private static final Logger LOG = Logger.getLogger(FrameProxy.class);
	
	private FrameProxy(){
	}
	
	/**
	 * 创建DAO层的代理对象
	 * @param cls
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T newProxyDaoInstance(final Class<T> cls){
		T proObj = (T)Proxy.newProxyInstance(cls.getClassLoader(),new Class[]{cls},new InvocationHandler(){
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
				SqlSession session = THREAD_LOCAL.get();
				boolean closeSession = false;
				if(session == null){
					closeSession = true;
					LOG.info("未获取到sqlSession");
					session = MybatisUtil.sqlSessionFactory.openSession(true);
				}
				T t = session.getMapper(cls);
				try {
					return method.invoke(t,args);
				}finally{
					if(closeSession){
						session.close();
					}
				}
			}
		});
		return proObj;
	}
	
	/**
	 * 创建Service层的代理对象
	 * @param cls
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T newProxyServiceInstance(final Object obj){
		final Class<?> objCls = obj.getClass();
		T proObj = (T)Proxy.newProxyInstance(objCls.getClassLoader(),objCls.getInterfaces(),new InvocationHandler(){
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
				Method instanceMethod = objCls.getMethod(method.getName(),method.getParameterTypes());
				Transactional transactional = instanceMethod.getAnnotation(Transactional.class);
				//是否自动处理事务
				boolean isTransactional = transactional != null;
				SqlSession sqlSession = MybatisUtil.sqlSessionFactory.openSession(!isTransactional);
				THREAD_LOCAL.set(sqlSession);
				try {
					Object result = method.invoke(obj,args);
					if(isTransactional){
						sqlSession.commit();
					}
					return result;
				} catch (Exception e) {
					if(isTransactional){
						sqlSession.rollback();
					}
					if(e instanceof InvocationTargetException){
						throw ((InvocationTargetException) e).getTargetException();
					}
					throw e;
				} finally {
					sqlSession.close();
				}
			}
		});
		return proObj;
	}
}
